// Tencent is pleased to support the open source community by making
// 蓝鲸智云 - 监控平台/日志平台 (BlueKing - Monitor/Log) available.
// Copyright (C) 2017-2022 THL A29 Limited, a Tencent company. All rights reserved.
// Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://opensource.org/licenses/MIT
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//

package testutils

import (
	"context"
	"time"

	"go.opentelemetry.io/collector/model/pdata"

	"github.com/TencentBlueKing/bk-collector/define"
)

type MetricsGenerator struct {
	ctx    context.Context
	cancel context.CancelFunc
	opts   define.MetricsOptions

	attributes pdata.AttributeMap
	resources  pdata.AttributeMap
}

func NewMetricsGenerator(opts define.MetricsOptions) *MetricsGenerator {
	attributes := FakeAttributeMap(opts.RandomAttributeKeys, opts.DimensionsValueType)
	resources := FakeAttributeMap(opts.RandomResourceKeys, opts.DimensionsValueType)
	ctx, cancel := context.WithCancel(context.Background())
	return &MetricsGenerator{
		ctx:        ctx,
		cancel:     cancel,
		attributes: attributes,
		resources:  resources,
		opts:       opts,
	}
}

func (g *MetricsGenerator) Stop() {
	g.cancel()
}

func (g *MetricsGenerator) Ch() chan pdata.Metrics {
	ch := make(chan pdata.Metrics, 128)
	data := g.Generate()
	n := 0
	go func() {
		defer close(ch)
		for {
			select {
			case <-g.ctx.Done():
				return

			case ch <- data:
				n++
				if n >= g.opts.Iteration {
					return
				}
				time.Sleep(g.opts.Interval)
			}
		}
	}()
	return ch
}

func (g *MetricsGenerator) Generate() pdata.Metrics {
	md := pdata.NewMetrics()
	rs := md.ResourceMetrics().AppendEmpty()
	rs.Resource().Attributes().UpsertString("service.name", "benchmark.service")
	g.resources.CopyTo(rs.Resource().Attributes())
	for k, v := range g.opts.Resources {
		rs.Resource().Attributes().UpsertString(k, v)
	}

	now := time.Now()
	for i := 0; i < g.opts.GaugeCount; i++ {
		metric := rs.InstrumentationLibraryMetrics().AppendEmpty().Metrics().AppendEmpty()
		metric.SetName(FakeString(12))
		if g.opts.MetricName != "" {
			metric.SetName(g.opts.MetricName)
		}
		metric.SetDataType(pdata.MetricDataTypeGauge)
		dp := metric.Gauge().DataPoints().AppendEmpty()
		dp.SetTimestamp(pdata.NewTimestampFromTime(now))
		dp.SetDoubleVal(float64(i))
		if g.opts.Value != nil {
			dp.SetDoubleVal(*g.opts.Value)
		}
		g.attributes.CopyTo(dp.Attributes())
		for k, v := range g.opts.Attributes {
			dp.Attributes().UpsertString(k, v)
		}
	}

	for i := 0; i < g.opts.CounterCount; i++ {
		metric := rs.InstrumentationLibraryMetrics().AppendEmpty().Metrics().AppendEmpty()
		metric.SetName(FakeString(12))
		if g.opts.MetricName != "" {
			metric.SetName(g.opts.MetricName)
		}
		metric.SetDataType(pdata.MetricDataTypeSum)
		dp := metric.Sum().DataPoints().AppendEmpty()
		dp.SetTimestamp(pdata.NewTimestampFromTime(now))
		dp.SetDoubleVal(float64(i))
		if g.opts.Value != nil {
			dp.SetDoubleVal(*g.opts.Value)
		}
		g.attributes.CopyTo(dp.Attributes())
		for k, v := range g.opts.Attributes {
			dp.Attributes().UpsertString(k, v)
		}
	}

	for i := 0; i < g.opts.HistogramCount; i++ {
		metric := rs.InstrumentationLibraryMetrics().AppendEmpty().Metrics().AppendEmpty()
		metric.SetName(FakeString(12))
		if g.opts.MetricName != "" {
			metric.SetName(g.opts.MetricName)
		}
		metric.SetDataType(pdata.MetricDataTypeHistogram)
		dp := metric.Histogram().DataPoints().AppendEmpty()
		dp.SetTimestamp(pdata.NewTimestampFromTime(now))
		dp.SetSum(float64(i))
		g.attributes.CopyTo(dp.Attributes())
		for k, v := range g.opts.Attributes {
			dp.Attributes().UpsertString(k, v)
		}
	}

	return md
}
